/**
 * @file A_log.c
 * @author MooN
 * @brief 
 * @version 0.0.1
 * @date 2022-08-24
 * 
 * @copyright (c) 2022 Moon
 * 
 */
#include "ulog.h"
#include "A_log.h"
#include "easyflash.h"
#include "Alog_default.h"
#include "ulog_easyflash.h"
/**
 * @brief 用户log初始化
 * 
 */
int AlogInit(signed short (*write)(char *, unsigned short), void (*console_flush)(void))
{
    ulog_init();
    ulog_console_backend_init(write, console_flush);
#if A_LOG_SAVE_FLASH == 1
    ulog_ef_log_lvl_set(ALOG_FLASH_LEVEL);
    ulog_ef_backend_init();
#endif
    return 0;
}

#if A_LOG_USE_SHELL == 1
#include "A_shell.h"

static void read_flash_log(size_t index, size_t size);
static void read_recent_flash_log(size_t size);
static void read_all_flash_log(void);
static int ulog_flash(uint8_t argc, char **argv);

/**
 * Read and output log to console.
 *
 * @param index index for saved log.
 *        Minimum index is 0.
 *        Maximum index is log used flash total size - 1.
 * @param size
 */

static void read_flash_log(size_t index, size_t size)
{
    /* 64 bytes buffer */
    uint32_t buf[16] = { 0 };
    size_t log_total_size = ef_log_get_used_size();
    size_t buf_size = sizeof(buf);
    size_t read_size = 0;

    /* word alignment for index and size */
    index = RT_ALIGN_DOWN(index, 4);
    size = RT_ALIGN_DOWN(size, 4);
    if (index + size > log_total_size)
    {
        Ashell_print("The output position and size is out of bound. The max size is %d.\r\n", log_total_size);
        return;
    }

    while (1)
    {
        if (read_size + buf_size < size)
        {
            ef_log_read(index + read_size, buf, buf_size);
            Ashell_print("%.*s", buf_size, buf);
            read_size += buf_size;
        }
        else
        {
            ef_log_read(index + read_size, buf, size - read_size);
            Ashell_print("%.*s", size - read_size, buf);
            /* output newline sign */
            Ashell_print(ULOG_NEWLINE_SIGN);
            break;
        }
    }
}
/**
 * Read and output recent log which saved in flash.
 *
 * @param size recent log size
 */
static void read_recent_flash_log(size_t size)
{
    size_t max_size = ef_log_get_used_size();

    if (size == 0)
    {
        return;
    }

    if (size > max_size)
    {
        Ashell_print("The output size is out of bound. The max size is %d.\r\n", max_size);
    }
    else
    {
        read_flash_log(max_size - size, size);
    }
}

/**
 * Read and output all log which saved in flash.
 */
static void read_all_flash_log(void)
{
    read_flash_log(0, ef_log_get_used_size());
}

/**
 * clean all log which in flash
 */
static void ulog_ef_log_size(void)
{
    size_t log_total_size = ef_log_get_used_size();

    Ashell_print("The log max size is %d.\r\n", log_total_size);
}

#if A_LOG_SHELL_TRANSFER == 1  
static enum ym_code _ym_send_begin(
    struct ym_ctx *ctx,
    uint8_t *buf,
    uint32_t len);
static enum ym_code _ym_send_data(
    struct ym_ctx *ctx,
    uint8_t *buf,
    uint32_t len);
static enum ym_code _ym_send_end(
    struct ym_ctx *ctx,
    uint8_t *buf,
    uint32_t len);

static uint32_t log_file_total_size, log_file_cur_size;

static enum ym_code _ym_send_begin(
    struct ym_ctx *ctx,
    uint8_t *buf,
    uint32_t len)
{
    char insert_0 = '\0';
	memset(buf, 0, len);
    log_file_total_size = ef_log_get_used_size();
    log_file_cur_size = 0;
    sprintf((char *)buf, "%s%c%u", LOG_FILE_NAME, insert_0, log_file_total_size);
    return YM_CODE_SOH;
}

static enum ym_code _ym_send_data(
    struct ym_ctx *ctx,
    uint8_t *buf,
    uint32_t len)
{
    uint32_t tx_len;
	
	memset(buf, 0, len);
    if ((log_file_cur_size + len) > log_file_total_size)
    {
        tx_len = log_file_total_size - log_file_cur_size;
    }
	else
	{
		tx_len = len;
	}
    /* word alignment for index and size */
    log_file_cur_size = RT_ALIGN_DOWN(log_file_cur_size, 4);
    tx_len = RT_ALIGN_DOWN(tx_len, 4);
    
    ef_log_read(log_file_cur_size, (uint32_t *)buf, tx_len);
	log_file_cur_size += tx_len;
    if (tx_len < len)
    {
        memset(buf + tx_len, 0x1A, len - tx_len);
        ctx->stage = YM_STAGE_FINISHING;
    }

    return YM_CODE_SOH;
}

static enum ym_code _ym_send_end(
    struct ym_ctx *ctx,
    uint8_t *buf,
    uint32_t len)
{
    memset(buf, 0, len);

    return YM_CODE_SOH;
}
#endif

static int ulog_flash(uint8_t argc, char **argv)
{
    if (argc >= 2)
    {
        if (!strcmp(argv[1], "read"))
        {
            if (argc >= 3)
            {
                read_recent_flash_log(atol(argv[2]));
            }
            else
            {
                read_all_flash_log();
            }
        }
        else if (!strcmp(argv[1], "clean"))
        {
            ulog_ef_log_clean();
        }
        else if (!strcmp(argv[1], "size"))
        {
            ulog_ef_log_size();
        }
#if A_LOG_SHELL_TRANSFER == 1   
        else if (!strcmp(argv[1], "upload"))
        {
            int res;
            struct ym_ctx rctx;
            Ashell_print("Please select a receive file and use Ymodem to receive.\r\n");
            res = ym_send_on_device(&rctx, _ym_send_begin, _ym_send_data, _ym_send_end, 100);
            if (!res)
            {
                Ashell_print("log upload success.\r\n");
            }
            else
            {
                Ashell_print("log upload fail,code = %d.\r\n", res);
            }
        }
#endif
        else
        {
#if A_LOG_SHELL_TRANSFER == 1            
            Ashell_print("Please input log <read num|clean|size|upload>.\r\n");
#else
            Ashell_print("Please input log <read num|clean|size>.\r\n");
#endif
        }
    }
    else
    {
#if A_LOG_SHELL_TRANSFER == 1            
            Ashell_print("Please input log <read num|clean|size|upload>.\r\n");
#else
            Ashell_print("Please input log <read num|clean|size>.\r\n");
#endif
    }
    return 1;
}

#if A_LOG_SHELL_TRANSFER == 1
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), log, ulog_flash, log <read num|clean|size|upload>:log by Flash backend);
#else
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), log, ulog_flash, log <read num|clean|size>:log by Flash backend);
#endif
#endif
